package net.csiit.tools.fileserver.core.storage;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import net.csiit.tools.fileserver.client.dto.FileInfo;
import net.csiit.tools.fileserver.common.dto.FileSaveLocation;
import net.csiit.tools.fileserver.common.dto.R;
import net.csiit.tools.fileserver.common.util.ObjectId;
import net.csiit.tools.fileserver.core.entity.FileEntity;
import net.csiit.tools.fileserver.core.repository.FileRepository;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author adam
 * @date 2023-06-12
 */
@Service
@Slf4j
public class FileService implements InitializingBean {

    private Set<String> supportTypes;

    @Value("${csiit.file.legalTypes}")
    private String legalType;
    @Value("${csiit.file.previewUrl}")
    private String previewUrl;

    @Autowired
    private FileRepository fileRepository;
    @Autowired
    private FileStorage fileStorage;

    public FileEntity getEntity(String fileId) {
        return fileRepository.findFirstByFileId(fileId);
    }

    public List<FileInfo> getFileInfos(List<String> fileIds) {
        if (CollectionUtils.isEmpty(fileIds)) {
            return Collections.emptyList();
        }

        List<FileEntity> fileEntities = fileRepository.findByFileIdIn(fileIds);
        return fileEntities.stream().map(v -> {
            FileInfo fileInfo = new FileInfo();
            BeanUtils.copyProperties(v, fileInfo);
            String previewAddr = String.format(previewUrl, fileInfo.getFileId());
            fileInfo.setPreviewUrl(previewAddr);
            fileInfo.setRealName(v.getOriginalName());
            fileInfo.setDownloadUrl(previewAddr);
            return fileInfo;
        }).collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public R<List<FileInfo>> uploadFiles(MultiValueMap<String, MultipartFile> fileMap) throws IOException {
        for (Map.Entry<String, List<MultipartFile>> entry : fileMap.entrySet()) {
            List<MultipartFile> multipartFiles = entry.getValue();
            for (MultipartFile file : multipartFiles) {
                String fileName = file.getOriginalFilename();
                String type = getFileType(fileName);
                if (!supportTypes.contains(type)) {
                    log.error("Unsupported file type : {}", type);
                    return R.fail("400", "不支持上传的文件类型! " + type);
                }
            }
        }

        List<FileEntity> entities = Lists.newArrayList();
        for (Map.Entry<String, List<MultipartFile>> entry : fileMap.entrySet()) {
            List<MultipartFile> multipartFiles = entry.getValue();
            for (MultipartFile file : multipartFiles) {
                entities.add(upload(file));
            }
        }

        fileRepository.saveAll(entities);

        return R.ok(entities.stream()
                .map(v -> {
                    FileInfo fileInfo = new FileInfo();
                    BeanUtils.copyProperties(v, fileInfo);
                    String previewAddr = String.format(previewUrl, fileInfo.getFileId());
                    fileInfo.setPreviewUrl(previewAddr);
                    fileInfo.setDownloadUrl(previewAddr);
                    fileInfo.setRealName(v.getOriginalName());
                    return fileInfo;
                }).collect(Collectors.toList())
        );
    }

    private String getFileType(String fileName) {
        String suffix = "none";
        if (StringUtils.contains(fileName, ".")) {
            suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        }

        return StringUtils.lowerCase(suffix);
    }

    private FileEntity upload(MultipartFile file) throws IOException {
        String fileId = new ObjectId().toHexString();
        String fileName = file.getOriginalFilename();
        String suffix = getFileType(fileName);

        String filePath = null;
        try (InputStream inputStream = file.getInputStream()) {
            filePath = fileStorage.saveFile(inputStream, suffix);
        } catch (IOException e) {
            if (!StringUtils.isEmpty(filePath)) {
                fileStorage.deleteFile(filePath);
            }
            throw e;
        }

        FileEntity entity = new FileEntity();
        entity.setCreateTime(new Date());
        entity.setFileFormat(suffix);
        entity.setFileId(fileId);
        entity.setLocation(FileSaveLocation.LOCAL.getLocation());
        entity.setOriginalName(fileName);
        entity.setPath(filePath);
        entity.setUpdateTime(new Date());
        return entity;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        supportTypes = Sets.newHashSet();
        if (!StringUtils.isEmpty(legalType)) {
            supportTypes.addAll(Lists.newArrayList(legalType.toLowerCase().split(",")));
        }
    }
}
